package socket5

import (
	"io"
	"net"
)

type ClientRequestMessage struct {
	CMD         Command
	Address     string
	Port        uint16
	AddressType Command
}

type Command = byte

const (
	IPV4_LENGHT = 4
	IPV6_LENGHT = 6
	PORT_LENGHT = 2
)

const (
	CmdConnect Command = 0x01
	CmdBind    Command = 0x02
	CmdUDP     Command = 0x03
)

type AddressType = byte

const (
	TypeIPV4   AddressType = 0x01
	TypeDoMain AddressType = 0x03
	TypeIPV6   AddressType = 0x04
)

func NewClientRequestMessage(conn net.Conn) (*ClientRequestMessage, error) {
	// 版本 命令 reseved 地址类型
	buf := make([]byte, 4)
	if _, err := io.ReadFull(conn, buf); err != nil {
		return nil, err
	}
	version, command, reserved, addType := buf[0], buf[1], buf[2], buf[3]
	if version != SOCKET_5_VERSION {
		return nil, ERROR_SOCKET_VERSION_SUPPORTED
	}

	if command != CmdConnect && command != CmdBind && command != CmdUDP {
		return nil, ERROR_COMMAND_NOT_SUPPORTED
	}

	if reserved != ReservedFild {
		return nil, ERROR_SOCKET_VERSION_SUPPORTED
	}

	if addType == TypeIPV4 || addType == TypeDoMain {
		var message ClientRequestMessage
		message.CMD = command
		message.AddressType = addType

		//fmt.Printf("请求地址类型为:%v\n", addType)

		switch addType {

		case TypeIPV6:
			buf = make([]byte, IPV6_LENGHT)
			if _, err := io.ReadFull(conn, buf); err != nil {
				return nil, err
			}
			ip := net.ParseIP(string(buf))
			message.Address = ip.String()
			//fmt.Println("TypeIPV6 地址为:" + ip.String())
		case TypeIPV4:
			buf = make([]byte, IPV4_LENGHT)
			if _, err := io.ReadFull(conn, buf); err != nil {
				return nil, err
			}
			ip := net.IP(buf)
			message.Address = ip.String()
			//fmt.Println("TypeIPV4 地址为:" + ip.String())
		case TypeDoMain:

			if _, err := io.ReadFull(conn, buf[:1]); err != nil {
				return nil, err
			}

			domain_length := buf[0]
			if domain_length > IPV4_LENGHT {
				buf = make([]byte, domain_length)
			}
			if _, err := io.ReadFull(conn, buf[:domain_length]); err != nil {
				message.Address = string(buf[:domain_length])
			}

			//fmt.Println("TypeDoMain 地址为:" + message.Address)
		}

		//读取 端口
		if _, err := io.ReadFull(conn, buf[:PORT_LENGHT]); err != nil {
			return nil, err
		}
		message.Port = (uint16(buf[0]) << 8) + uint16(buf[1])
		return &message, nil
	} else {
		return nil, ERRADDRESS_TYPE_NOT_SUPPORTED
	}
}

type ReplyType = byte

const (
	ReplySuccess ReplyType = iota
	ReplyServerFailure
	ReplyConnectionNotAllowed
	ReplyNetworkUnreachable
	ReplyHostUnreachable
	ReplyConnectionRefused
	ReplyTTLExpired
	ReplyCommandNotSupported
	ReplyAddressTypeNotSupported
)

func WriteRequestSuccessMessage(conn net.Conn, ip net.IP, port uint16) error {

	addressType := TypeIPV4
	if len(ip) == IPV6_LENGHT {
		addressType = TypeIPV6
	}

	// Write version, reply success, reserved, address type
	_, err := conn.Write([]byte{SOCKET_5_VERSION, ReplySuccess, ReservedFild, addressType})
	if err != nil {
		return err
	}

	// write bind IP(IPV4/IPV6)
	if _, err := conn.Write(ip); err != nil {
		return err
	}
	// write bind port
	buf := make([]byte, 2)
	buf[0] = byte(port >> 8)
	buf[1] = byte(port - uint16(buf[0]))
	_, err = conn.Write(buf)
	return err
}

func WriteRequestFailureMessage(conn net.Conn, replyType ReplyType) error {
	_, err := conn.Write([]byte{SOCKET_5_VERSION, replyType, ReservedFild, TypeIPV4, 0, 0, 0, 0, 0, 0})
	return err
}
